#include <iostream>
#include <time.h>
#include <stdlib.h>
#include "../include/index_compressor.h"

using namespace std;

IndexCompressor::IndexCompressor() {
}

IndexCompressor::IndexCompressor(EComprType type) :
  type_(type) {
}

IndexCompressor::~IndexCompressor() {

}

int IndexCompressor::write_uniry_bits(int bit_num, 
				      char* code_list, 
				      int &len,
				      int &offset) {
  if (bit_num == 0) {
    return offset;
  }

  char tempc = 0;
  if (offset == 0) {
    tempc = 255;
    if (bit_num >= 8) {
      memset(code_list + len, tempc, bit_num / 8);
      len += bit_num / 8;
    }
    offset = bit_num % 8;
    if (offset) {
      tempc = (1 << offset) - 1;
      code_list[len] = tempc;
    }
    return offset;
  }
  if (offset + bit_num <= 8) {
    tempc = (1 << bit_num) - 1;
    tempc <<= offset;
    code_list[len] |= tempc;
    offset = (offset + bit_num) % 8;
    if (offset == 0) {
      len++;
    }
  } else {
    tempc = (1 << (8 - offset)) - 1;
    tempc <<= offset;
    code_list[len] |= tempc;
    len++;
    int new_offset = (offset + bit_num) % 8;
    int byte_num = 0;
    if (new_offset == 0) {
      byte_num = (offset + bit_num) / 8 - 1;
      tempc = 255;
      memset(code_list + len, tempc, byte_num);
      len += byte_num;
      offset = new_offset;
    } else {
      byte_num = (offset + bit_num) / 8 - 1;
      tempc = 255;
      if (byte_num > 0) {
        memset(code_list + len, tempc, byte_num);
        len += byte_num;
      }

      offset = new_offset;
      code_list[len] = (1 << offset) - 1;
    }
  }

  return offset;
}

int IndexCompressor::write_binary_bits(int value, int bit_num, char* code_list,
				       int &len, int &offset) {
  int new_offset = (offset + bit_num) % 8;
  int tempLen = 0;
  if (new_offset) {
    tempLen = (offset + bit_num) / 8 + 1;
  } else {
    tempLen = (offset + bit_num) / 8;
  }

  if (value == 0) {
    offset = new_offset;
    if (offset)
      len += tempLen - 1;
    else
      len += tempLen;
    return offset;
  }

  if (tempLen <= 4) {
    value <<= offset;
    char* pValue = (char*) &value;
    code_list[len] |= pValue[0];
    for (int i = 1; i < tempLen; i++)
      code_list[len + i] = pValue[i];
    offset = new_offset;
    if (offset)
      len += tempLen - 1;
    else
      len += tempLen;
  } else {
    int part = value << offset;

    char* pPart = (char*) &part;
    code_list[len] |= pPart[0];
    memcpy(code_list + len + 1, pPart + 1, 3);
    part = value >> (32 - offset);
    pPart = (char*) &part;
    code_list[len + 4] = pPart[0];
    offset = new_offset;
    len += 4;
  }
  return offset;
}

int IndexCompressor::encode(int value, char* code_list, int& len, int& offset,
			    EComprType type) {
  int bit_num = 0;
  int bit_value = 0;
  int pre_bit_num = 0;
  int prefix = 0;
  int postfix = 0;
  int temp = 0;

  int bit_num2 = 0;
  int prefix2 = 0;
  int postfix2 = 0;

  switch (type) {
  case GAMMA:
    value++;
    if (value == 1) {
      offset = (offset + 1) % 8;
      if (offset == 0) {
        len++;
      }
      return offset;
    }
    temp = value;
    bit_num = 0;
    while (temp) {
      temp >>= 1;
      bit_num++;
    }
    bit_num--;

    temp = 1 << bit_num;

    prefix = temp - 1;
    postfix = value - temp;

    offset = write_uniry_bits(bit_num, code_list, len, offset);
    offset = (offset + 1) % 8;
    if (offset == 0) {
      len++;
    }
    offset = write_binary_bits(postfix, bit_num, code_list, len, offset);
    break;
  case THETA:
    value++;
    if (value == 1) {
      offset = (offset + 1) % 8;
      if (offset == 0) {
        len++;
      }
      return offset;
    }
    temp = value;
    bit_num = 0;
    while (temp) {
      temp >>= 1;
      bit_num++;
    }
    bit_num--;
    temp = 1 << bit_num;

    prefix = 0;
    postfix = value - temp;

    temp = bit_num + 1;

    while (temp) {
      temp >>= 1;
      bit_num2++;
    }
    bit_num2--;
    temp = 1 << bit_num2;
    prefix2 = temp - 1;
    postfix2 = bit_num - prefix2;
    prefix = (postfix2 << (bit_num2 + 1)) + prefix2;

    offset = write_binary_bits(prefix, bit_num2 * 2 + 1, code_list, len, offset);
    offset = write_binary_bits(postfix, bit_num, code_list, len, offset);
    break;

  case VGOLOMB:
    value++;
    pre_bit_num = (value - 1) / GOLOMBBASE;
    offset = write_uniry_bits(pre_bit_num, code_list, len, offset);
    offset = (offset + 1) % 8;
    if (offset == 0) {
      len++;
    }
    postfix = (value - 1) % GOLOMBBASE;
    bit_num = 4;

    offset = write_binary_bits(postfix, bit_num, code_list, len, offset);
    break;

  case BYTECODEEX:
    bit_value = 0;
    if (value < 128) {
      bit_num = 1;
    } else if (value < 16384) {
      bit_num = 2;
      bit_value = 1;
    } else if (value < 2097152) {
      bit_num = 3;
      bit_value = 3;
    } else {
      bit_num = 4;
      bit_value = 7;
    }
    value <<= bit_num;
    value += bit_value;
    memcpy(code_list + len, (char*) &value, bit_num);
    len += bit_num;

    break;
  case ONEBYTECHAR:
    code_list[len] = value;
    len++;
    break;
    
  default: cerr << "Error:\t code type" << endl;
  }
  return offset;
}

int IndexCompressor::encode(int value, char* code_list, int& len, int& offset) {
  return encode(value, code_list, len, offset, type_);
}

int IndexCompressor::read_uniry_bits(const char* code_list, int &len,
    int &offset) {
  int bit_num = 0;
  int tempValue = 0;
  bool noend = true;
  while (noend) {
    memcpy((char*) &tempValue, code_list + len, 4);
    int testValue = 1 << offset;
    int testPos = offset;

    while (testPos < 32 && testValue & tempValue) {
      testPos++;
      testValue <<= 1;
    }
    if (testPos != 32) {
      bit_num += testPos - offset;
      len += testPos / 8;
      offset = testPos % 8;
      noend = false;
    } else {
      bit_num += 32 - offset;
      len += 4;
      offset = 0;
    }
  }

  return bit_num;
}

int IndexCompressor::read_binary_bits(const char* code_list, int &len, int &offset,
    int bit_num) {
  int value = 0;
  if (offset + bit_num < 32) {
    memcpy((char*) &value, code_list + len, 4);
    int temp = (1 << (offset + bit_num)) - 1;
    value &= temp;
    value >>= offset;
  } else if (offset + bit_num == 32) {
    memcpy((char*) &value, code_list + len, 4);
    value >>= offset;
  } else {
    int part = 0;
    memcpy((char*) &part, code_list + len, 3);
    part >>= offset;
    value = part;

    part = 0;
    memcpy((char*) &part, code_list + len + 3, 2);
    part &= (1 << (bit_num + offset - 24)) - 1;
    part <<= 24 - offset;
    value += part;
  }
  len += (offset + bit_num) / 8;
  offset = (offset + bit_num) % 8;
  return value;
}

int IndexCompressor::decode(const char* code_list, int &len, int &offset) {
  return decode(code_list, len, offset, type_);
}

int IndexCompressor::decode(const char* code_list, int &len, int &offset,
    EComprType type) {
  int value = 0;
  int prefix = 0;
  int postfix = 0;
  int bit_num = 0;

  if (type == GAMMA || type == THETA) {
    bit_num = 0;
    prefix = 0;
    postfix = 0;

    bit_num = read_uniry_bits(code_list, len, offset);
    if (bit_num == 0) {
      offset = (offset + 1) % 8;
      if (offset == 0) {
        len++;
      }
      return 1;
    }
    prefix = 1 << bit_num;
    offset = (offset + 1) % 8;
    if (offset == 0) {
      len++;
    }

    postfix = read_binary_bits(code_list, len, offset, bit_num);
    value = prefix + postfix;
    if (type == GAMMA)
      return value - 1;
    bit_num = value - 1;
    prefix = 1 << bit_num;
    postfix = read_binary_bits(code_list, len, offset, bit_num);
    value = prefix + postfix - 1;
  } else if (type == VGOLOMB) {
    //prefix
    bit_num = 0;
    bit_num = read_uniry_bits(code_list, len, offset);
    prefix = bit_num * GOLOMBBASE;
    //space 
    offset = (offset + 1) % 8;
    if (offset == 0) {
      len++;
    }
    //postfix
    postfix = read_binary_bits(code_list, len, offset, 4);
    value = prefix + postfix;
  } else if (type == BYTECODEEX) {
    unsigned char bit_value = code_list[len] % 0x10;
    if ((bit_value & 0x1) == 0) {
      bit_num = 1;
    } else if ((bit_value & 0x2) == 0) {
      bit_num = 2;
    } else if ((bit_value & 0x4) == 0) {
      bit_num = 3;
    } else if ((bit_value & 0x8) == 0) {
      bit_num = 4;
    }

    memcpy((char*) &value, code_list + len, bit_num);
    value >>= bit_num;
    len += bit_num;
  } else if (type == ONEBYTECHAR) {
    value = code_list[len];
    len++;
  }
  return value;
}

int IndexCompressor::print_bits(const char* code_list, int len, int offset) {
  unsigned char tempc = 0;
  for (int i = 0; i < len; i++) {
    tempc = 1;
    while (tempc) {
      if (tempc & code_list[i]) {
        cout << 1;
      } else {
        cout << 0;
      }
      tempc <<= 1;
    }
    cout << " ";
  }

  tempc = 1;
  int k = 0;
  while (tempc && k < offset) {
    if (tempc & code_list[len]) {
      cout << 1;
    } else {
      cout << 0;
    }
    tempc <<= 1;
    k++;
  }
  cout << endl;

  return 0;
}

